package main

//tset
import (
	"fmt"
	"reflect"
)

type BlockMessage struct {
}

type Transaction struct {
	Hash    []byte
	From    int8
	BlockId int
}

func (t *Transaction) IsEmpty() bool {
	return reflect.DeepEqual(t, Transaction{})
}

type Line struct {
	InstituteLine [4][]Transaction //机构交易队列
	instituteNum  [4]int           //队列长度排序
	total         int              //剩余机构数量
	lotalTransNum int              //总交易数量
	//lastOperation [2]int           //上次出队位置
}

type Quintet struct {
	BlockId int
	Number  int
	Trans   []Transaction
	Compute int8
	Storage []int8
}

type TQuintet struct {
	Quin   []Quintet
	Offset int64
}

func (l *Line) Push(trans Transaction) { //入队
	l.InstituteLine[trans.From] = append(l.InstituteLine[trans.From], trans)

	l.lotalTransNum++
}

func (l *Line) Init() { //排序
	l.total = 4
	temp := -1
	location := -1
	length := [4]int{len(l.InstituteLine[0]), len(l.InstituteLine[1]), len(l.InstituteLine[2]), len(l.InstituteLine[3])}
	if Debug == 1 {
		fmt.Println("剩余：", length)
	}
	for i := 0; i < 4; i++ {
		for j := 0; j < 4; j++ { //选出最大的
			if temp < length[j] {
				temp = length[j]
				location = j
			}
		}
		if temp == 0 {
			l.instituteNum[i] = -1 //该机构没有交易
			l.total--
			length[location] = -1 //确定位置后不参与排序
		} else {
			l.instituteNum[i] = location
			length[location] = -1

		}
		temp = -1
		location = -1
	}
	if Debug == 1 {
		fmt.Println("排序", l.instituteNum)
	}

}

func (l *Line) Pull() (trans []Transaction) { //出队
	trans = make([]Transaction, 3)
	if l.total == 4 || l.total == 3 {
		trans[0] = l.InstituteLine[l.instituteNum[0]][0] //从最长的前三个里面取
		trans[1] = l.InstituteLine[l.instituteNum[1]][0]
		trans[2] = l.InstituteLine[l.instituteNum[2]][0]
		l.InstituteLine[l.instituteNum[0]] = l.InstituteLine[l.instituteNum[0]][1:len(l.InstituteLine[l.instituteNum[0]])] //取完删掉
		l.InstituteLine[l.instituteNum[1]] = l.InstituteLine[l.instituteNum[1]][1:len(l.InstituteLine[l.instituteNum[1]])]
		l.InstituteLine[l.instituteNum[2]] = l.InstituteLine[l.instituteNum[2]][1:len(l.InstituteLine[l.instituteNum[2]])]
	} else if l.total == 2 {
		trans[0] = l.InstituteLine[l.instituteNum[0]][0]
		trans[1] = l.InstituteLine[l.instituteNum[0]][1]
		trans[2] = l.InstituteLine[l.instituteNum[1]][0]
		l.InstituteLine[l.instituteNum[0]] = l.InstituteLine[l.instituteNum[0]][2:len(l.InstituteLine[l.instituteNum[0]])]
		l.InstituteLine[l.instituteNum[1]] = l.InstituteLine[l.instituteNum[1]][1:len(l.InstituteLine[l.instituteNum[1]])]
	} else if l.total == 1 {
		trans[0] = l.InstituteLine[l.instituteNum[0]][0]
		trans[1] = l.InstituteLine[l.instituteNum[0]][1]
		trans[2] = l.InstituteLine[l.instituteNum[0]][2]
		l.InstituteLine[l.instituteNum[0]] = l.InstituteLine[l.instituteNum[0]][3:len(l.InstituteLine[l.instituteNum[0]])]
	}
	l.Init()
	return trans
}

func mySqrt(x int8) int8 {
	var i int8
	for i = 0; i <= x; i++ {
		res := i * i
		if res == x {
			return i
		} else if res > x {
			return i - 1
		}
	}
	return -1
}

//已知数据块的来源，求计算、存储的机构
func (q *Quintet) Distribute() {
	if len(q.Trans) == 3 {
		A := q.Trans[0].From
		B := q.Trans[1].From
		C := q.Trans[2].From
		if A != B && A != C { //当三个机构各不同时
			temp := int8(uint8(q.Trans[0].Hash[0]) % 3) //随便抽一个来计算
			if temp == 0 {
				q.Compute = A
			} else if temp == 1 {
				q.Compute = B
			} else {
				q.Compute = C
			}
			q.Storage = append(q.Storage, 6-A-B-C) //剩下一个来存储
		}
		if A == B && A != C { //两个不同时
			q.Compute = q.Trans[0].From //第一家来算
			a := 6 - A - C
			b := 14 - A*A - C*C
			D := ((a + (mySqrt(2*b - a*a))) / 2) % 4
			//fmt.Println(a, b, mySqrt(2*b-a^2))
			//q.Storage = append(q.Storage, (a+(2*b-a^2)^(1/2))/2)//A+B=6-C-D
			//q.Storage = append(q.Storage, (a-(2*b-a^2)^(1/2))/2)//A^2+B^2=14-C^2-D^2
			q.Storage = append(q.Storage, D)
			q.Storage = append(q.Storage, (6 - A - C - D))
		}
		if A == B && A == C {
			q.Compute = q.Trans[0].From
			q.Storage = append(q.Storage, (A+1)%4)
			q.Storage = append(q.Storage, (A+2)%4)
			q.Storage = append(q.Storage, (A+3)%4)
		}
	} else {
		if len(q.Trans) == 1 {
			q.Compute = q.Trans[0].From
			q.Storage = append(q.Storage, (q.Trans[0].From+int8(uint8(q.Trans[0].Hash[0])%3+1))%4)
			/*} else {                           直接拆成一个存副本算了 或者挪到下一个区块？
			if q.Trans[0].From == q.Trans[1].From{
				q.Compute = q.Trans[0].From
				q.Storage = append(q.Storage, int8(uint8(q.Trans[0].Hash[0]) % 3))
				q.Storage = append(q.Storage, int8(uint8(q.Trans[1].Hash[0]) % 3))
			}else{
				q.Compute = int8(uint8(q.Trans[0].Hash[0]) % 3)
			}*/
		}
	}

}

func (q *Quintet) Print() {
	fmt.Print("区块：", q.BlockId, "\t")
	fmt.Print("序号：", q.Number, "\t")
	fmt.Print("来源：")
	for _, t := range q.Trans {
		fmt.Print(t.From, " ")
	}
	fmt.Print("\t计算：", q.Compute)
	fmt.Print("\t存储: ", q.Storage, "\n")
}

//根据交易数组确定冗余方案
func GenerateDateDistribute(b Block) (q TQuintet) {
	var line Line
	var quintetlinte []Quintet
	var tq TQuintet
	var BlockId int
	fmt.Println("计算区块", b.BlockId)
	transses := b.transses
	BlockId = b.BlockId
	j := 0
	for _, trans := range transses { //交易入队
		line.Push(trans)
	}

	//fmt.Println(len(line.InstituteLine[0]), len(line.InstituteLine[1]), len(line.InstituteLine[2]), len(line.InstituteLine[3]))

	line.Init()
	for i := 0; i < len(transses)/3; i++ { //交易出队
		var quintet Quintet
		quintet.Number = j
		quintet.BlockId = BlockId
		j++
		quintet.Trans = append(quintet.Trans, line.Pull()...)
		quintet.Distribute() //确定冗余方式
		//quintet.Print()
		quintetlinte = append(quintetlinte, quintet)
	}
	if len(transses)%3 != 0 {
		if line.total == 1 {
			for i := 0; i < len(transses)%3; i++ { //交易出队
				var quintet Quintet
				quintet.Number = j
				quintet.BlockId = BlockId
				j++
				quintet.Trans = append(quintet.Trans, line.InstituteLine[line.instituteNum[0]][i])
				quintet.Distribute() //确定冗余方式
				//quintet.Print()
				quintetlinte = append(quintetlinte, quintet)
			}
		} else {
			for i := 0; i < len(transses)%3; i++ { //交易出队
				var quintet Quintet
				quintet.Number = j
				quintet.BlockId = BlockId
				j++
				quintet.Trans = append(quintet.Trans, line.InstituteLine[line.instituteNum[i]][0])
				quintet.Distribute() //确定冗余方式
				//quintet.Print()
				quintetlinte = append(quintetlinte, quintet)
			}
		}
	}
	tq.Quin = append(tq.Quin, quintetlinte...)
	return tq

}
